﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Threading;
using System.Timers;
using LEDControllerTools.Commands;
using PIC.Communication;
using LEDControllerTools;

namespace LEDControllerTools.Controller
{

    public class Controller : IControllable
    {
        public event BatteryVoltageEventHandler BatteryUpdate;
        public event ToggledEventHandler PinToggled;
        public event StatusUpdateEventHandler StatusUpdate;

        private readonly IExecutable _batteryVoltageCommand;
        private readonly IDictionary<string, IToggleable> _digitalOutputCommands = new Dictionary<string, IToggleable>();
        private readonly IBatteryVoltageResponder _batteryVoltageResponse;

        private Controller(ICommandFactory commandFactory)
        {
            // Get the commands and requests off the command factory
            _batteryVoltageCommand = commandFactory.GetBatteryCommand();
            _digitalOutputCommands = commandFactory.GetDigitalOutputCommands();
            _batteryVoltageResponse = commandFactory.GetBatteryVoltageResponse();

            // Set up the event for a battery voltage status update
            _batteryVoltageResponse.BatteryUpdate += OnBatteryUpdate;

            // Set up the events for when a pin has been toggled
            foreach (var digitalOutputCommand in _digitalOutputCommands.Values)
                digitalOutputCommand.Toggled += digitalOutputCommand_Toggled;

            // Set a 10 min timer to request the battery voltage
            var batteryRequestTimer = new System.Timers.Timer(10 * 60 * 10); //10 min in ms
            batteryRequestTimer.Elapsed += timer_Elapsed;
            batteryRequestTimer.Start();

        }


        public IToggleable this[string id]
        {
            get { return _digitalOutputCommands[id]; }
        }


        public void RequestBattery()
        {
            OnStatusUpdate("Requesting Battery Voltage...");
            _batteryVoltageCommand.Execute();

            SendDummyBatteryVoltage(); // Fake voltage for testing
        }


        private void SendDummyBatteryVoltage()
        {
            var rand = new Random();
            var dec = rand.NextDouble();
            var voltage = Convert.ToSingle(9.0 + dec);
            OnBatteryUpdate(this, new BatteryVoltageEventArgs(voltage, DateTime.Now));
        }


        public static IControllable GetInstance(ICommandFactory commandFactory)
        {
            var controller = new Controller(commandFactory);
            return controller;
        }


        public string[] GetOutputs()
        {
            return _digitalOutputCommands.Keys.ToArray();
        }


        public void Wait(int milliSeconds)
        {
            OnStatusUpdate(string.Format(@"Waiting {0} ms", milliSeconds));
            Thread.Sleep(milliSeconds);
        }


        public void IssueDigitalOutCommand(object sender, DigitalOutCommandEventArgs e)
        {
            if (e.Value)
                _digitalOutputCommands[e.Command].On();
            else
                _digitalOutputCommands[e.Command].Off();
        }


        void digitalOutputCommand_Toggled(object sender, ToggledEventArgs e)
        {

            var definition = ControllerDefinition.GetDefinition();

            // The command which was issued
            var command = definition.DigitalCommands[e.Id];
            OnStatusUpdate(string.Format("Command: {0} set to {1}", e.Id, e.IsOn));

            // Send an event to each pin envolved in this command
            foreach (var port in command.WorksWith)
                OnPinToggled(port, new ToggledEventArgs(port.Name, e.IsOn));
        }


        private void OnPinToggled(object sender, ToggledEventArgs e)
        {
            OnStatusUpdate(string.Format(@"{0} is {1}", e.Id, e.IsOn ? "On" : "Off"));
            if (PinToggled != null)
                PinToggled(sender, e);
        }


        private void timer_Elapsed(object sender, ElapsedEventArgs e)
        {
            RequestBattery();
        }
        

        public void OnBatteryUpdate(object sender, BatteryVoltageEventArgs e)
        {
            OnStatusUpdate(string.Format(@"Battery Voltage of {0} received at {1}", e.Voltage.ToString(CultureInfo.InvariantCulture), e.Received.ToString(CultureInfo.InvariantCulture)));
            if (BatteryUpdate != null)
                BatteryUpdate(sender, e);
        }

        
        private void OnStatusUpdate(string message)
        {
            if (StatusUpdate != null)
                StatusUpdate(this, message);
        }





    }
}
